﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using F2Enum.Core;
using System.IO;
using F2Enum.TestApp.Lib;
using F2Enum.Library;
using NUnit.Framework;

namespace F2Enum.UnitTests
{


    [TestFixture]
    public class F2EnumTests
    {
        private const string Filepath = "Data/Test.txt";
        private static int _lineNumber;
        private static IList<MapperClass<TestClass>> _mappings;


        [SetUp]
        public void Init()
        {


            _mappings = new List<MapperClass<TestClass>>
                            {
                                new MapperClass<TestClass>(e => e.TestClass1.Champ1, 1, 7),
                                new MapperClass<TestClass>(e => e.Champ2, 1, 7)
                            };
            _lineNumber = File.ReadAllLines(Filepath).Length;
        }

        [Test]
        public void ConvertExpression()
        {
            var concurrentList = new ConcurrentBag<bool>();
            var cancelation = new CancellationTokenSource();
            var scheduler = TaskScheduler.Current;
            var dataflowOption = new ExecutionDataflowBlockOptions
                                     {
                                         MaxDegreeOfParallelism = Environment.ProcessorCount,
                                         CancellationToken = cancelation.Token
                                     };
            var bufferedBLoc = new BufferBlock<int>(dataflowOption);
            var transformationBloc = new TransformBlock<int, bool>(i => true, dataflowOption);
            var actionBloc = new ActionBlock<bool>(i => concurrentList.Add(i), dataflowOption);
            bufferedBLoc.LinkTo(transformationBloc);
            transformationBloc.LinkTo(actionBloc);
            bufferedBLoc.Completion.ContinueWith(r => transformationBloc.Complete());
            transformationBloc.Completion.ContinueWith(r => actionBloc.Complete());
            var watch = Stopwatch.StartNew();
            Task.Factory.StartNew(() =>
                                      {
                                          for (int i = 0; i < 1000000; i++)
                                          {
                                              bufferedBLoc.SendAsync(i);
                                          }
                                      }, TaskCreationOptions.LongRunning).ContinueWith(r => bufferedBLoc.Complete());
            actionBloc.Completion.Wait();
            watch.Stop();
            Console.WriteLine(watch.Elapsed);

        }


        [Test]
        public void IsAllObjectGeted()
        {
            var watch = Stopwatch.StartNew();
            var objects = F2EnumLibrary.ConvertToEnumerable(Filepath, _mappings).ToArray();
            watch.Stop();
            Debug.WriteLine(watch.Elapsed);
            Assert.AreEqual(objects.Length, _lineNumber);
        }

        [TearDown]
        public void Clean()
        {
            _mappings = null;
        }


        [Test]
        public void TestDynamicExpression()
        {
            Delegate r1 = null;
            var cl = new TestClass();
            Expression<Func<TestClass, object>> test = e => e.Champ1;
            Expression<Func<TestClass, object>> test2 = e => e.Champ2;
            var member = test.Body as MemberExpression;
            PropertyInfo propertyInfo = null;
            var param = Expression.Parameter(typeof(TestClass), "e");
            if (member != null)
            {
                 propertyInfo = member.Member as PropertyInfo;
                if (propertyInfo != null)
                {
                    var param2 = Expression.Parameter(propertyInfo.PropertyType, "param2");
                    var param3 = Expression.Parameter(propertyInfo.PropertyType, "param3");
                    var champ1Assign = Expression.Assign(Expression.Property(param, member.Member.Name), param2);
                    var var  = test2.Body as MemberExpression;
                    if (var  != null)
                    {
                        var champ2Assign =
                            Expression.Assign(Expression.Property(param, var.Member.Name),
                                              param3);
                        var blocExpression = Expression.Block(champ1Assign, champ2Assign);
                        var result = Expression.Lambda(blocExpression, param, param2, param3);
                        r1 = result.Compile();

                    }
                }
            }
            Action<TestClass, string> action = (t, s) =>
                                                   {
                                                       t.Champ1 = s;
                                                   };
            if (r1 != null)
            {
                var watch = Stopwatch.StartNew();
                for (int i = 0; i < 1000000; i++)
                {
                    r1.DynamicInvoke(cl, "toto", "titi");
                }
                watch.Stop();
                Debug.WriteLine("time with dynamic action : {0}", watch.Elapsed);
                watch.Restart();
                for (int i = 0; i < 1000000; i++)
                {
                    cl.Champ1 = "toto";
                    cl.Champ2 = "titi";
                }
                Debug.WriteLine("time with normal instantiation : {0}", watch.Elapsed);
                watch.Restart();
                for (int i = 0; i < 1000000; i++)
                {
                    action(cl, "toto");
                }
                Debug.WriteLine("time with static action : {0}", watch.Elapsed);
            }


        }

        [Test]
        public void GetTimeForReadingFile()
        {
            var watch = Stopwatch.StartNew();
            var list = new List<TestClass>();
            using (var fs = new FileStream(Filepath, FileMode.Open, FileAccess.Read))
            {
                using (var lines = new StreamReader(fs))
                {
                    string line;
                    while (!string.IsNullOrEmpty((line = lines.ReadLine())))
                    {
                        var t = new TestClass
                                    {
                                        Champ1 = "toto",
                                        Champ2 = "titi",
                                        TestClass1 = new TestClass
                                                         {
                                                             Champ1 = "titi",
                                                             TestClass1 = new TestClass
                                                                              {
                                                                                  Champ1 = "titi"

                                                                              }

                                                         }
                                    };
                        list.Add(t);
                    }

                }

            }
            watch.Stop();
            Debug.WriteLine(watch.Elapsed);
        }

        [Test]
        public void TestDynamicMethod()
        {
            string[,] gradeArray = { { "chemistry", "history", "mathematics" }, { "78", "61", "82" } };

            System.Linq.Expressions.Expression arrayExpression =
                System.Linq.Expressions.Expression.Constant(gradeArray);

            // Create a MethodCallExpression that represents indexing
            // into the two-dimensional array 'gradeArray' at (0, 2).
            // Executing the expression would return "mathematics".
            System.Linq.Expressions.MethodCallExpression methodCallExpression =
                System.Linq.Expressions.Expression.ArrayIndex(
                    arrayExpression,
                    System.Linq.Expressions.Expression.Constant(0),
                    System.Linq.Expressions.Expression.Constant(2));

            Console.WriteLine(methodCallExpression.Method.ToString());
        }
    }
}


